/*  _______         ____    __         ___    ___
 * \    _  \       \    /  \  /       \   \  /   /       '   '  '
 *  |  | \  \       |  |    ||         |   \/   |         .      .
 *  |  |  |  |      |  |    ||         ||\  /|  |
 *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
 *  |  |  |  |      |  |    ||         ||    |  |         .      .
 *  |  |_/  /        \  \__//          ||    |  |
 * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
 *                                                      /  \
 *                                                     / .  \
 * dumb.txt - DUMB library reference.                 / / \  \
 *                                                   | <  /   \_
 * See readme.txt for general information on         |  \/ /\   /
 * DUMB and how to set it up.                         \_  /  > /
 *                                                      | \ / /
 * If you are new to DUMB, see howto.txt.               |  ' /
 *                                                       \__/
 */


***********************************
*** Include Files and Libraries ***
***********************************


dumb.h

   Include this if you only want the core DUMB library functions. You will
   be able to load music files and render them into memory buffers at your
   own pace. The core library is completely portable, and as such does not
   access hardware; you must relay the sound data to the sound card yourself.
   A stdio file input module is available, but you must actively register it
   if you wish to use it (see dumb_register_stdfiles()); if you do not
   register it, it will not be linked into your executable. You must register
   it in order to load stand-alone music files.

   Optimised: libdumb.a  (-ldumb)
   Debugging: libdumbd.a (-ldumbd)


aldumb.h

   Include this if you wish to use DUMB with Allegro. This will provide you
   with functions to play DUHs back through Allegro's audio streams and embed
   music files in Allegro datafiles. A file input module using Allegro's
   packfiles is provided; you have a choice between this and the stdio
   module. You will be able to load datafiles containing music files no
   matter which file input module you register, or even if you register no
   file input module. However, you must register a file input module in order
   to load stand-alone files.

   Optimised: -laldmb -lalleg -ldumb
   Debugging: -laldmd -lalld -ldumbd

   libaldmb.a or libaldmd.a must be linked in first, so the symbols can be
   resolved when linking in the other two libraries.


***********************************
*** Library Clean-up Management ***
***********************************


int dumb_atexit(void (*proc)(void));

   Registers a function to be called at the end of your program. You can
   register multiple functions to be called, and the one you register last
   will be called first. If you try to register the same function twice, the
   second attempt will have no effect.

   See fnptr.txt for help with function pointers.

   You must call dumb_exit() before exiting your program for this to work
   properly. The library itself registers functions with dumb_atexit(), so it
   is important to call dumb_exit() even if you do not use dumb_atexit()
   yourself.

   This function will return zero on success. It will return zero when
   trying to install the same function twice. If it fails through lack of
   memory, it will return nonzero. Generally you can ignore the return code;
   in the worst case some memory will not be freed at the end. If it is
   crucial that your function be called (e.g. to shut down some hardware or
   save critical data), then you should call your function manually at the
   end of the program instead of registering it here - or use the stdlib
   function atexit(), guaranteed under ANSI C to succeed for at least 32
   functions.


void dumb_exit(void);

   You should call this before exiting your program if you have used any part
   of DUMB in the program. Some parts of DUMB will allocate memory, and this
   function will free it all up.

   More specifically, this function will call any functions that have been
   registered with dumb_atexit(). If a part of DUMB needs shutting down, the
   shutdown procedure will have been registered in this way.

   dumb_exit() will, of course, also call any functions you registered with
   dumb_atexit() yourself.

   After a call to dumb_exit(), the list of functions is erased. If you are
   not ready to exit your program, you can start using DUMB anew as if your
   program had just started. (Note that not everything will be reset in
   practice - dumb_resampling_quality will retain whatever you set it to, for
   example, though you should not assume it will.)

   If you only need to call dumb_exit() once at the end of the program, you
   can use the following to register dumb_exit() with stdlib.h atexit():

      #include <stdlib.h>

      atexit(&dumb_exit);

   Then dumb_exit() will be called for you when your program exits. This is
   the recommended method, since it will ensure clean-up even if your program
   aborts. You should only call dumb_exit() manually if you need to shut DUMB
   down prematurely, or if atexit() is unavailable for one reason or another.


*****************************
*** Sequential File Input ***
*****************************


   DUMB provides a strictly sequential file input system which uses the
   DUMBFILE struct. "Strictly sequential" means you cannot seek backwards.
   However, the system will keep track of how many bytes you have read,
   enabling you to seek forwards. DUMBFILEs provide a convenient error
   detection system, so you do not have to check the return value from every
   function call in the way you do with the ANSI C functions.

   Note that DUMBFILEs cannot be used for output, nor can they be used
   portably for text files.

   If an error occurs when reading data from a DUMBFILE, the DUMBFILE will
   become inoperative. All subsequent activities on the DUMBFILE will return
   error codes without attempting to read from the file. The position in the
   file will also be forgotten. You can find out if this has happened at any
   stage with the dumbfile_error() function. You are still required to close
   the DUMBFILE, and the return value from dumbfile_close() will tell you if
   an error has occurred.

   This system allows you to input large chunks of your file, neither
   checking every return value nor wasting time accessing a file that has
   already experienced an error. However, before you allocate an amount of
   memory or read in a quantity of data depending on previous input from the
   file, you should always check that such input was valid. In particular you
   should passing zero or negative numbers to malloc() or dumbfile_getnc().

   DUMBFILEs can be hooked. In other words, you can specify your own
   functions to do the work of reading from a file. While DUMB contains two
   modules for this purpose, it does not set them up for you automatically.
   In most cases you must register one of these modules yourself, or provide
   your own module. See register_dumbfile_system(), dumb_register_stdfiles()
   and dumb_register_packfiles().


void register_dumbfile_system(DUMBFILE_SYSTEM *dfs);

   Use this function to register a set of functions for use by the DUMBFILEs
   (a DUMBFILE system). The DUMBFILE_SYSTEM struct contains the following
   fields:

      void *(*open)(const char *filename);
      int (*skip)(void *f, long n);
      int (*getc)(void *f);
      long (*getnc)(char *ptr, long n, void *f);
      void (*close)(void *f);

   See fnptr.txt for help with function pointers such as these.

   Your 'open' function should open the file specified and return a pointer
   to a struct representing the open file. This pointer will be passed to
   your other functions as 'f'. Your 'close' function should close the file
   and free all memory pointed to by 'f'. Note that the 'close' operation
   should never be able to fail; if you are calling a function with a return
   value, you can generally ignore it.

   Your 'getc' function should read one byte from the file and return its
   value in the range 0 to 255. If an error occurs, you should return -1. Do
   not worry about remembering that an error has occurred; DUMB will do that
   for you.

   'skip' is for skipping parts of the file, and should skip n bytes,
   returning 0 on success or any other number on failure. 'getnc' should read
   n bytes from the file, store them at 'ptr', and return the number of bytes
   read (n on success, fewer on failure). However, these two functions are
   optional, and you should only provide them if the operations can be done
   more efficiently than with repeated calls to your 'getc' function. If this
   is not the case, specify NULL for 'skip', 'getnc' or both, and DUMB will
   use your 'getc' function to do the work.

   Once you have written all your functions, you need to create a
   DUMBFILE_SYSTEM struct to hold them, and pass its pointer to
   register_dumbfile_system().

   The DUMBFILE_SYSTEM struct must be permanent. In other words, it must be
   either global or static, and you should not modify it later. DUMB will not
   make its own copy.

   You will most likely create your own struct to represent the open file,
   but do not be tempted to specify that struct in the function prototypes
   and pacify the compiler warnings by casting your function pointers. There
   exist computer systems where a (void *) pointer and a (MY_STRUCT *)
   pointer are represented differently in memory, and a cast of such a
   pointer causes a tangible conversion to take place. If you cast the
   function pointers, the computer cannot know when such a conversion is
   necessary. Instead, use the following structure:

      int myskip(void *f, long n)
      {
         FILE *file = f;
         /* Do some stuff with 'file' */
         return something;
      }

   If you need examples, have a look at the two existing DUMBFILE systems in
   dumb/src/core/stdfile.c and dumb/src/allegro/packfile.c.


DUMBFILE *dumbfile_open(const char *filename);

   Open the specified file for input. You must pass the DUMBFILE pointer
   whenever you wish to operate on this file. When you have finished with the
   file, you must pass it to dumbfile_close().

   Before you use this function, make sure you have registered a DUMBFILE
   system. See register_dumbfile_system(), dumb_register_stdfiles() and
   dumb_register_packfiles().


DUMBFILE *dumbfile_open_ex(void *file, DUMBFILE_SYSTEM *dfs);

   This function is provided for more specialised use. You should create a
   DUMBFILE_SYSTEM specially for the purpose. Its 'open' field is irrelevant;
   for neatness, set it to NULL, unless you are using this DUMBFILE_SYSTEM
   with register_dumbfile_system() as well.

   When you have called this function, the DUMBFILE struct it returned can be
   used as normal. The specified DUMBFILE_SYSTEM will be used for all input,
   with 'file' passed to your 'skip', 'getc' and 'getnc' functions as 'f'.
   This can be used, for example, to read from an already open file.

   Note that the position will always be initialised to 0 for this DUMBFILE.
   This means for example that offsets in the file do not need adjusting when
   embedding data in a larger file.

   There are two ways to use this function. If you want 'file' to persist
   after using a DUMBFILE returned by this function, you should make sure the
   'close' field in the DUMBFILE is set to NULL. When the DUMBFILE is closed,
   'file' will be left alone, and you can and should deal with it yourself
   when the DUMBFILE has been closed.

   Alternatively, you can provide a 'close' function to get rid of 'file' for
   you when the DUMBFILE is closed. If you do this, you should not otherwise
   use 'file' after a call to this function.

   If dumbfile_open_ex() has to return NULL, owing to lack of memory, then
   your 'close' function will be called if provided. In other words, if you
   have provided a 'close' function, then you no longer need to worry about
   'file' whether this function succeeds or not.

   See dumb/src/helpers/stdfile.c and dumb/src/allegro/packfile.c for
   examples of how to use this function. Neither provides a 'close' function,
   so I hope my explanation here will suffice. If not, please feel free to
   contact me so I can make the explanation clearer and help you do what you
   want to do. Contact details are at the end of this file.


long dumbfile_pos(DUMBFILE *f);

   Returns the number of bytes read from the DUMBFILE (or skipped) since it
   was opened, or -1 if an error has occurred while reading.


int dumbfile_skip(DUMBFILE *f, long n);

   Skips n bytes of the specified DUMBFILE. Returns zero on success.


int dumbfile_getc(DUMBFILE *f);

   Reads one byte from the DUMBFILE and returns it in unsigned format (from 0
   to 255). If an error occurs, or occurred before, this function returns -1.


int dumbfile_igetw(DUMBFILE *f);

   Reads two bytes from the DUMBFILE and combines them into a word ranging
   from 0 to 65535. The first byte read is the least significant byte, as
   with Intel processors. This function returns -1 on error.


int dumbfile_mgetw(DUMBFILE *f);

   Reads two bytes from the DUMBFILE and combines them into a word ranging
   from 0 to 65535. The first byte read is the most significant byte, as
   with the Apple Macintosh. This function returns -1 on error.


long dumbfile_igetl(DUMBFILE *f);

   Reads four bytes from the DUMBFILE and combines them into a long integer
   ranging from -2147483648 to 2147483647. The first byte read is the least
   significant byte, as with Intel processors. This function returns -1 on
   error, but -1 is also a valid return value. After a call to this function,
   you can use dumbfile_error() to find out if an error occurred.


long dumbfile_mgetl(DUMBFILE *f);

   Reads four bytes from the DUMBFILE and combines them into a long integer
   ranging from -2147483648 to 2147483647. The first byte read is the most
   significant byte, as with the Apple Macintosh. This function returns -1 on
   error, but -1 is also a valid return value. After a call to this function,
   you can use dumbfile_error() to find out if an error occurred.


unsigned long dumbfile_cgetul(DUMBFILE *f);

   Reads an unsigned (nonnegative) integer from the DUMBFILE. The integer is
   stored in a condensed format where smaller numbers use less space:

           0 to 127         1 byte
         128 to 16383       2 bytes
       16384 to 2097151     3 bytes
     2097152 to 268435455   4 bytes
   268435456 to 4294967295  5 bytes

   This format is the same as that used for the times between notes in MIDI
   files.

   If an error occurs, this function returns (unsigned long)(-1), but that
   may be a valid return value. After a call to this function, you can use
   dumbfile_error() to find out if an error occurred.


signed long dumbfile_cgetsl(DUMBFILE *f);

   Reads a signed integer from the DUMBFILE. The integer is stored in a
   condensed format where numbers closer to zero use less space:

           -64 to 63          1 byte
         -8192 to 8191        2 bytes
      -1048576 to 1048575     3 bytes
    -134217728 to 134217727   4 bytes
   -2147483648 to 2147483647  5 bytes

   If an error occurs, this function returns -1, but -1 is also a valid
   return value. After a call to this function, you can use dumbfile_error()
   to find out if an error occurred.


long dumbfile_getnc(char *ptr, long n, DUMBFILE *f);

   Reads n bytes from the DUMBFILE and stores them at 'ptr'. Note that the
   pointer is to a series of chars. You may also use this function to read in
   a series of signed chars or unsigned chars (which are both officially
   distinct types from char), but do not use this to read ints, structs or
   any other data type from the file. Integers must be read one at a time
   using dumbfile_igetl(), dumbfile_cgetul(), etc. To load a struct in, you
   must read each field separately using an appropriate function for each
   one. For complicated datatypes, you can simplify this process by writing a
   function for each struct.


int dumbfile_error(DUMBFILE *f);

   This function returns -1 if an error has occurred with the specified
   DUMBFILE, or 0 if all is well.


int dumbfile_close(DUMBFILE *f);

   This function closes the DUMBFILE, after which the pointer will be
   invalid. dumbfile_close() returns the value that dumbfile_error() would
   have returned, which is -1 if an error occurred while reading or 0
   otherwise. Regardless of the return value, the file will always be closed
   properly.


*******************************
*** stdio File Input Module ***
*******************************


void dumb_register_stdfiles(void);

   This function registers the stdio file input module for use by DUMBFILEs.
   FILE structs and their corresponding functions, as defined by the ANSI C
   header stdio.h, will be used internally for all DUMBFILE input (unless
   opened with dumbfile_open_ex()).

   This must be called before dumbfile_open() is used, or else an alternative
   system must be registered (see register_dumbfile_system() and
   dumb_register_packfiles()).


DUMBFILE *dumbfile_open_stdfile(FILE *p);

   If you have a stdio FILE struct representing an open file, you can call
   this if you wish to read from it using a DUMBFILE. This is useful when you
   need to pass a DUMBFILE struct to a library function, to read an embedded
   music file for example. When you close the DUMBFILE, you can continue
   using the FILE struct to read what follows the embedded data.


********************************
*** Signal Type Registration ***
********************************






   NOTE: SINCE THIS IS NOT TO BE THE INTRODUCTION TO DUMB, SHOULD THE
   FOLLOWING INFORMATION BE MOVED TO ANOTHER FILE?






   If you are lazy, then don't bother to read this section. Simply follow
   these steps:

      1. Call all the dumb_register_sigtype_*() functions. You must do this
         after dumb_init(), but before you try to call load_duh(). If you try
         to load any DUH files beforehand, the library will fail to load
         them.

      2. Run your program, and make sure the DUH file was successfully loaded
         and played.

      3. Comment out the first dumb_register_sigtype_*() function call.

      3.1. If the DUH file is still loaded and played, remove this function
           call.
      3.2. If the DUH file was not loaded and played, uncomment and keep this
           function call.

      4. Repeat Step 3 for the other function calls.

   Alternatively, the musician might have told you which of the functions you
   need to call.

   If you are the epitome of laziness, stop after Step 1. However, if you do
   this, your executable may contain unnecessary code.

   If, on the other hand, you are interested in how all this works, then read
   on.

   A DUH file comprises a number of 'signals'. A signal may be thought of as
   a 'black box'; commands go in, sound comes out. A signal may in turn call
   upon other signals to generate sound, and then do something with this
   sound to generate its own output.

   For example, here are the contents of a simple DUH file:
      Signal #0 SEQU
      Signal #1 SAMP
      Signal #2 SAMP
      Signal #3 SAMP

   'SEQU' and 'SAMP' are the signal types. 'SAMP' designates a sample, so
   Signals #1, #2 and #3 are simple recordings. For instance, Signal #1 could
   be a bass drum, Signal #2 a snare drum and Signal #3 a high hat. When
   invoked, these signals simply output their respective percussion sounds.

   'SEQU' designates a sequence. Signal #0 will therefore generate its sound
   by taking the output waveforms from the other signals and mixing them in
   at appropriate times. For example:

      0.00 Signal #1

      0.50 Signal #2

      1.00 Signal #1
      1.25           Signal #3
      1.50 Signal #2 Signal #3
      1.75           Signal #3
      2.00 Signal #1
      2.25           Signal #3
      2.50 Signal #2 Signal #3
      2.75           Signal #3
      3.00 Signal #1

      3.50 Signal #2 Signal #3

   The numbers down the left are times. Those with a good sense of rhythm
   will be able to see that this represents a rather lame, highly cheesy pop
   beat. Experienced gurus will also realise how painfully slow the beat is,
   given that the times are in seconds. But enough of that.

   A DUH is played by taking the output from Signal #0 and sending it through
   to the sound card via an Allegro audio stream. If you do not know what an
   audio stream is, suffice it to say that you must call Allegro's
   install_sound() function, and an audio stream uses one voice (hence one
   voice per DUH file). If you still don't know what I'm on about, read up on
   Allegro's digital sound system and then come back here. Alternatively,
   read porting.txt for details on how to use DUMB without Allegro.

   In reality, DUH files are likely to be much more complicated than the
   above example. Sequences may call upon other 'sub-sequences' to generate
   their sound, so the above beat might be used repeatedly by a
   'super-sequence' to generate a more complex (but still lame) piece of
   music.

   The DUH player library is split into two main parts - the core and the
   basic signal types. The core is very simple. It does not know what 'SAMP'
   or 'SEQU' mean. In fact it is not familiar with any signal type. It only
   contains generic code for dealing with signals and generating output. By
   itself, it cannot load or play any DUH file.

   In addition to the core, the player library comprises a few basic signal
   types, including 'SAMP' and 'SEQU'. In order to use a signal type, you
   must 'register' this signal type by calling its corresponding
   dumb_register_sigtype_*() function. These functions are listed below,
   along with descriptions of the signals.

   If you do not register a signal type, the code for that signal type will
   not be linked into your executable file. That means DUMB will not become
   bloated with age; new features will not add to the size of your executable
   unless you use them. Dynamically linked libraries will still increase in
   size.

   If you try to load a DUH file that uses a signal type you haven't
   registered, the library will not crash. It will simply fail to load the
   DUH file. In fact, an unrecognised or corrupt signal will prevent the
   library from reading the rest of the file - but remember, DUH files are
   always (intended to be) generated from other formats, so you will never
   lose any data this way.

   If you are unsure which signal types a DUH uses, get in contact with the
   author of the DUH file, or register them all and remove them one by one to
   find out which are required (as described in the steps for lazy people at
   the start of this section).

   The great advantage of DUMB over other music systems is that it enables
   you to create your own signals. Filters, synthesisers and compression
   algorithms can all be set up this way. Programming and audio experience
   are useful, but the editor provides a way to create your own signals even
   if you don't know C. More information is available in the on-line help in
   the editor.

   If you are interested in the gory details of how a signal works, or if you
   wish to brave creating your own without the help of the editor, then see
   the section entitled Signal Design.


void dumb_register_sigtype_sample(void);

   Registers the sample signal type ('SAMP'). This signal deals with samples,
   or recordings, in the DUH file. All samples are monaural; a combining
   signal can be used to combine two of these into a stereo sample. The DUMB
   library will cater for more than two channels to a certain extent, but it
   is currently limited by Allegro's audio streams.


void dumb_register_sigtype_combining(void);

   Registers the combining signal type ('COMB'). This signal takes a set of
   monaural signals and combines them into one signal with multiple channels.
   Stereo samples can be set up this way.


void dumb_register_sigtype_stereopan(void);

   Registers the stereo pan signal type ('SPAN'). This signal takes a
   monaural sample and sources it at the stereo position you specify,
   provided the DUH is played in stereo (or, more precisely, provided stereo
   output is requested of the signal).


void dumb_register_sigtype_sequence(void);

   Registers the sequence signal type ('SEQU'). This signal sequences the
   sound from other signals. It is what turns a random collection of
   recordings of single notes into a complete piece of music.


**********************
*** DUH Management ***
**********************


DUH *load_duh(const char *filename);

   Loads a .duh file. Before you call this, make sure you have registered all
   the necessary signal types (see 'Signal Registration'). The DUH is
   returned to you; you will need to pass it to various functions. When you
   have finished with it, call unload_duh() to remove it from memory.

   There is no special need to check that this function succeeds. All other
   functions can safely be called with a null pointer, which means your music
   will simply not play if it could not be loaded.


DUH *read_duh(DUMBFILE *f);

   Reads a DUH from an already open file, and leaves the file open so you can
   read subsequent data from the file if you wish. Otherwise this function is
   identical to load_duh().


void unload_duh(DUH *duh);

   Removes a DUH from memory. You must call this for all DUHs you load,
   making sure they're not playing at the time.


long duh_get_length(DUH *duh);

   Returns the length of a DUH; 65536 represents one second. This value is
   simply lifted from the DUH struct, so it may not truly correspond to the
   time for which the DUH will generate sound. However, if the musician is
   any good - or if the code that calculated this value is written properly -
   you can assume it represents the point at which the DUH first loops, or
   else it allows time for any final flourish to be appreciated. It is used
   by the Winamp plug-in to decide when to stop.


*******************************
*** Impulse Tracker Support ***
*******************************


int dumb_it_max_to_mix;

   Specifies the maximum number of samples DUMB will mix at any one time.
   The default number is 64. Regardless of this value, all samples will
   continue to be processed up to an internal maximum of 128 (slightly
   simplified), and cut samples will sound again as soon as the congestion
   clears. Samples are prioritised by final volume, after all factors
   affecting the volume of a sample have been considered.

   If you play two or more IT files at once, this value represents the
   maximum number of samples for each one. You will have to reduce it further
   if your computer cannot keep up.


DUH *dumb_load_it(const char *filename);

   Loads the specified Impulse Tracker file, encapsulating it in a DUH
   struct. No signal types need be registered for this to work. The length
   will be set to the point at which the music first loops (see
   duh_get_length()).

   Once the file is loaded, it can be treated exactly the same as any other
   DUH in memory.


DUH *dumb_read_it(DUMBFILE *f);

   Reads an Impulse Tracker file from an already open DUMBFILE. This leaves
   the DUMBFILE open, but the DUMBFILE may not be positioned at the end of
   the IT data. If you are embedding an IT in another file, you are advised
   to store the size of the IT file and make up for it at the end using
   dumbfile_pos().

   Otherwise, this function is identical to dumb_load_it().


*******************************
*** DUH Rendering Functions ***
*******************************


   Use these functions to generate samples from a DUH. First you call
   duh_start_renderer() with the DUH, the number of channels you want and the
   position at which you want to start. Then you use duh_render() to generate
   the samples. You can call duh_render() as many times as you like, and it
   will generate as many or as few samples as you require. When you have
   finished, call duh_end_renderer().


DUH_RENDERER *duh_start_renderer(DUH *duh, int n_channels, long pos);

   Starts a DUH_RENDERER off. This is the struct you can use to get samples
   from a DUH. This function does not generate any samples; you must pass the
   struct to duh_render() for that. When you have finished with it, you must
   pass it to duh_end_renderer(). You can use as many DUH_RENDERER structs as
   you like at the same time.

   Currently, n_channels can only be 1 or 2, for monaural and stereo sound
   respectively. The debugging library will cause your program to abort if
   you pass anything else. Future versions will be enhanced to support more
   channels as soon as someone needs them.

   When specifying the position, 0 represents the start of the DUH, and 65536
   represents one second. Unlike most other music systems, DUMB will always
   make sure every note is there right from the start (provided any custom
   signal types are properly designed). In other words, you can start a DUH
   at a point halfway through a long note, and you will still hear the long
   note.


long duh_render(
   DUH_RENDERER *dr,
   int bits, int unsign,
   float volume, float delta,
   long size, void *sptr
);

   Generates some samples. Pass the DUH_RENDERER as returned by
   duh_start_renderer(). Pass the number of bits, which should be 8 or 16. If
   unsign is nonzero, the samples will be unsigned (centred on 0x80 or 0x8000
   for 8 bits and 16 bits respectively). If unsign is zero, the samples will
   be signed.

   Allegro's audio streams always take unsigned samples. 8-bit .wav files
   always take unsigned samples. 16-bit .wav files always take signed
   samples.

   The volume is a float. 1.0f is the pseudo-maximum. If you pass 1.0f, any
   properly designed DUH will play nice and loud, but will not clip. You can
   pass a greater volume if you like, but be prepared for clipping to occur.
   Of course you can pass smaller values to play the DUH more quietly, and
   this will also resolve clipping issues in badly designed DUHs.

   Use delta to control the speed of the output signal. If you pass 1.0f, the
   resultant signal will be suitable for a 65536-Hz sampling rate (which
   isn't a commonly used rate). The most common sampling rates are 11025 Hz,
   22050 Hz, 44100 Hz and 48000 Hz. You can work out the required delta value
   as follows:

      delta = 65536.0f / sampling_rate;

   If you then increase this value, the DUH will speed up and increase in
   pitch. If you decrease it, the DUH will slow down and decrease in pitch.

   This function will attempt to render 'size' samples. In most cases it will
   succeed. However, if the end of the DUH is reached, it may render fewer.
   The number of samples rendered will be returned. Therefore, if the return
   value is less than the value of 'size' passed, you know the DUH has
   finished. It is safe to continue calling duh_render() if you wish, and it
   will continually return 0. However, if you wish to do this, you will
   probably have to fill the rest of the buffer with silence, which is 0 for
   signed, 0x80 for 8-bit unsigned or 0x8000 for 16-bit unsigned.

   The samples will be placed at sptr. Use an array of chars for 8 bits or an
   array of shorts for 16 bits. Stereo samples will be interleaved, left
   first. Your array should contain at least (size * n_channels) elements of
   the appropriate bit resolution.

   From an aesthetic standpoint if nothing else, it is wise to use the C
   qualifiers 'signed' or 'unsigned' depending on whether the samples are
   signed or unsigned. This is also convenient if you wish to process the
   samples further yourself.


long duh_renderer_get_position(DUH_RENDERER *dr);

   Tells you what position a DUH_RENDERER is up to, or -1 if it is invalid
   (perhaps owing to lack of memory). As usual, 65536 is one second. Note
   that this is a whole number, whereas a fractional part is stored
   internally; the sample will not be continuous if you terminate the
   DUH_RENDERER and then reinitiate it with the same position.


void duh_end_renderer(DUH_RENDERER *dr);

   Terminates a DUH_RENDERER. Be sure to call this when you've finished with
   one.


***********************************
*** Signal Design Helper Values ***
***********************************


DUMB_SEMITONE_BASE

   When a 'delta' value is required, you can use DUMB_SEMITONE_BASE to
   control the pitch. Use pow(DUMB_SEMITONE_BASE, n) to transpose up by n
   semitones. To transpose down, use negative n.


DUMB_QUARTERTONE_BASE

   When a 'delta' value is required, you can use DUMB_QUARTERTONE_BASE to
   control the pitch. Use pow(DUMB_QUARTERTONE_BASE, n) to transpose up by n
   quartertones. To transpose down, use negative n.


DUMB_PITCH_BASE

   When a 'delta' value is required, you can use DUMB_PITCH_BASE to control
   the pitch accurately. Use pow(DUMB_PITCH_BASE, n) to transpose up by n
   units, where 256 units represent one semitone. This scale is used for the
   'pitch' in a sequence (SEQU).


************************************
*** Signal Design Function Types ***
************************************


   In order to design your own signal type, you will have to write six
   functions to be linked into your program. They are described below.

   See fnptr.txt for help with function pointer types such as these.


typedef void *(*DUH_LOAD_SIGNAL)(DUH *duh, DUMBFILE *file);

   Write a function conforming to this type which loads or creates all the
   data required by your signal. You may use this, for instance, to load a
   sample. You will be reading directly from the DUH file, so make sure you
   read exactly the quantity of data stored in the file for this signal. You
   should allocate memory (multiple blocks if you like), and return a pointer
   referencing all these data. Your data will be stored, and provided
   whenever this signal is handled. Be careful about using global data, since
   your load_signal function will be called more than once for different
   signals of the same type (e.g. for signals which are both samples but are
   different samples).

   On failure you should return NULL. Please take the possibility of failed
   memory allocation seriously, especially when loading large quantities of
   data. You should make sure all allocated memory is freed before you return
   with failure. See the standard signal types for examples of how to do this
   elegantly using your unload_signal function. On failure, you need not
   worry about reading the right quantity of data from the file.

   Do not close the file under any circumstances.

   Often you will write this function before you have written any code to
   write a DUH file using this signal. If this is the case, don't worry; you
   can write this function free-style, and then the function itself will
   serve as a file format reference when you come to create the file.

   Note that a null return value will always be interpreted as failure. If
   this type of signal does not need to load any data, you should not provide
   a load_signal function at all. The absence of this function will convey
   the intended message to the library.


typedef void *(*DUH_START_SAMPLES)(
   DUH *duh,
   void *signal,
   int n_channels,
   long pos
);

   Write a function conforming to this type. Every time your signal is
   required to produce some output, this will be called first. The 'signal'
   parameter is the same one you returned from your load_signal function. The
   'n_channels' parameter specifies how many channels you will need to render
   sound in. In general you should support one or two, although sometimes
   only one is necessary, depending on how your signal is used. If you can
   write generic code to support more channels, then do so. Store the number
   of channels for use later, unless you're only permitting one value.

   If your signal does not support more than two channels, you are
   responsible for making sure it is never invoked with more than two;
   likewise if your signal only supports one, then make sure it is never
   invoked with more than one. In general you only need to make sure the DUH
   file is never rendered with too many channels (see duh_start_renderer()
   and al_start_duh()). It may be prudent to use Allegro's ASSERT() macro to
   catch bugs of this kind.

   The 'pos' parameter specifies where to start, 65536 being one second into
   the signal. Even if you do not intend to start in the middle of the signal
   yourself, you should support this as it will be used when a DUH is not
   played from the beginning.

   This function should make the necessary preparations in order to render a
   string of samples. All your preparations should be stored in allocated
   memory, to which you return a pointer; this allows several instances of
   the same signal to play at the same time. You will not be given the 'duh'
   and 'signal' parameters again, so you must store them if you need them in
   the render_samples or free_samples functions.

   Once again, on the rare occasions on which you do not need such data (e.g.
   a white noise signal), you should not provide a start_samples function. A
   null return code is interpreted as failure, and your music will be lacking
   notes (which is better than crashing on undetected memory allocation
   failure).


typedef void (*DUH_SET_PARAMETER)(
   void *sampinfo,
   unsigned char id, long value
);

   Some signals can take parameters. For example, a stereo pan signal allows
   you to specify the position at which to source the sample, and you might
   want to choose the cut-off frequency for a low-pass filter. Such
   parameters are set on an instance-by-instance basis, not globally.
   Therefore, if you have any parameters, you should place default values for
   them in the data you initialise in start_samples. Then you should write a
   function conforming to this type, and have it change the parameters when
   the correct IDs are passed (see below).

   Each of your parameters should be identified by a single byte, which will
   be passed in the 'id' parameter. When one of your parameters is correctly
   identified by id, you should change the parameter's value in the data you
   returned from your start_samples function. These data are available via
   the 'sampinfo' parameter. If you do not recognise the ID passed, you may
   wish to log the situation using Allegro's TRACE() macro (which will
   compile away to nothing unless you define DEBUGMODE), since it signifies a
   fault in the DUH file.

   Take some care in deciding what should be a parameter and what should be
   arranged through the use of separate signals (of the same type). For
   example, if you are doing a low-pass filter, you will need a source signal
   on which to apply the filter; this signal's index should be loaded by the
   load_signal function, so you will create separate filter signals for the
   separate source signals. However, the cut-off frequency for the filter is
   more suitably stored in a parameter. Here are some guidelines:

      Continuous values, or discrete quantities, can be stored in parameters.
      Examples: filter cut-off, echo time, stereo pan position.

      Discrete indices or references should be loaded by load_signal.
      Examples: references to other signals.

   Another way of looking at it is as follows:

      If a value could be changed halfway through playing the signal, then
      consider storing it in a parameter.

      If a value needs to be known by the start_samples function and cannot
      change later, then consider loading it in the load_signal function.

   If your signal has no parameters, do not provide a set_parameters
   function. Attempts to change parameters for this signal will not cause a
   crash, but will cause the operation to be logged using Allegro's TRACE()
   macro if the debugging library is used to play the DUH.


typedef long (*DUH_RENDER_SAMPLES)(
   void *sampinfo,
   float volume, float delta,
   long size, sample_t **samples
);

   Write a function conforming to this type. It should render a series of
   samples into the array of buffers provided (see below). You are passed
   'sampinfo' as returned by start_samples. The 'samples' parameter points to
   an array of buffers, one for each channel. You can get a pointer to the
   buffer for channel #n with:

      sample_t *buffer = samples[n];

   As you can see, samples are of type sample_t, which is typedeffed as a
   signed int (32 bits). Your waveform should be centred on 0 and should peak
   at no more than +/- 32768, or +/- 0x8000, given a volume of 1.0f. If your
   waveform goes higher, it will be clipped later on (you do not need to test
   for this yourself). Please do not read any special meaning into the volume
   parameter; it is simply a factor to be multiplied into each sample. If you
   wish to adjust the tone, use a parameter (see DUH_SET_PARAMETER).

   In this function, you should render 'size' samples into the buffer for
   each channel. Return the number of samples actually rendered, which will
   be fewer than 'size' if you run out of samples (e.g. if you reach the end
   of a non-looping sample). Never stop short unless the signal ends, and do
   not overrun under any circumstances. Update the 'sampinfo' data so that
   the samples generated by the next call to render_samples will continue
   seamlessly from the samples generated this time.

   The delta parameter governs the speed at which your signal will play back.
   Higher values of delta should cause the speed and pitch to increase, as
   with duh_render(). In general, if delta is 1.0f then your waveform should
   be suitable for playback at a sampling rate of 65536 Hz. If you wish to
   bend this rule, then be careful - usually it is wiser to adjust your
   thinking in other parts.

   For example, consider a sample recorded at 44100 Hz, stored in a sample
   signal. If a delta of 1.0f is passed to this signal's render function, the
   output will be exactly the same as the original sample - a signal at
   44100 Hz. So it breaks the rule.

   Ah, but it doesn't. Instead, it is wiser to consider the sample stored in
   the DUH file as being sampled at 65536 Hz. Once we consider this, it is
   clear that the output is suitable for playback at 65536 Hz, so we do not
   break the rule. Now it is a simple matter of adjusting all the pitches in
   the sequence to compensate for this. See duhtech.txt for details on how to
   do this when you are writing the DUH file.

   Note that the position that was passed to the start_samples function is
   65536 for one second into the signal as played with delta 1.0f. So, with a
   sample signal, this position is interpreted as the index of the sample on
   which to start. If delta is 2.0, only half a second of the resultant
   output will have been skipped.

   Once again, please do not read any special meaning into the delta
   parameter. The effect of doubling delta should be virtually the same as
   the effect of not doubling delta but resampling the output. If you wish to
   adjust the tone for different pitches, use a parameter.

   This function is compulsory. Silent signals only increase processor and
   memory usage, and we are not Microsoft-Intel evangelists.


typedef void (*DUH_END_SAMPLES)(void *sampinfo);

   Write a function conforming to this type. It will be called when a signal
   stops playing. The parameter points to the data you returned from the
   start_samples function, and here you should simply free the memory up.

   This function should be provided, always if, and only if, start_samples is
   present. The debugging library will check you get this right.


typedef void (*DUH_UNLOAD_SIGNAL)(void *signal);

   Write a function conforming to this type. It will be called when the DUH
   is removed from memory. It should free all memory allocated by your
   load_signal function. The parameter is the same pointer that you returned
   from load_signal.

   If you only ever use this signal type with make_duh(), and not with
   dumb_register_sigtype(), then the following does not apply.

   If load_signal is present, unload_signal should also be present; if you
   did not provide a load_signal function, then you should not provide an
   unload_signal function either. The debugging library will check you get
   this right.


**********************************
*** Signal Design Registration ***
**********************************


void dumb_register_sigtype(DUH_SIGTYPE_DESC *desc);

   When you have written all the functions that represent your signal, you
   will need to register them with the library before it will be able to load
   your DUH file. Use this function. The DUH_SIGTYPE_DESC struct contains the
   following fields:

      long type;
      DUH_LOAD_SIGNAL    load_signal;
      DUH_START_SAMPLES  start_samples;
      DUH_SET_PARAMETER  set_parameter;
      DUH_RENDER_SAMPLES render_samples;
      DUH_END_SAMPLES    end_samples;
      DUH_UNLOAD_SIGNAL  unload_signal;

   You need to create a DUH_SIGTYPE_DESC struct and pass its pointer to
   dumb_register_sigtype(). The struct must be in permanent memory. In other
   words, it must be either global or static, and you should not modify it
   later. DUMB will not make its own copy.

   'type' should be a four-character string encoded with DUMB_ID(), for
   example DUMB_ID('M','E','O','W'). By convention it should be upper case,
   and padded with spaces if you do not use all four characters. However, you
   do not have to stick to this.

   If you are not providing a function, specify NULL for the corresponding
   function pointer.


**********************************
*** Signal Rendering Functions ***
**********************************


   When you are designing your own signals, you will often want to retrieve
   samples from another signal in the DUH. This signal's index ('sig') should
   be loaded by your load_samples function. You can use the following
   functions to obtain the samples.


DUH_SIGNAL_SAMPINFO *duh_signal_start_samples(
   DUH *duh, int sig, int n_channels, long pos
);

   Specifies where you want to start rendering. This function returns a
   DUH_SIGNAL_SAMPINFO struct, which you need to pass to the other functions.
   You can use as many DUH_SIGNAL_SAMPINFOs at once as you like.

   Pass the DUH and the index of the signal whose samples you want to obtain
   ('sig'). Specify how many channels you want, and where you want to start
   rendering (65536 represents one second).

   There is no special need to check that this function succeeds. The other
   functions are safe to call with null pointers. However, checking the
   return value can make your code more efficient.

   Be sure to call duh_signal_end_samples() when you've finished.


void duh_signal_set_parameter(
   DUH_SIGNAL_SAMPINFO *signal_sampinfo,
   unsigned char id, long value
);

   Sets a parameter for the signal whose samples are being rendered by
   signal_sampinfo.




   Calls the set_parameter function for the instance started by
   duh_signal_start_samples of the signal whose details you passed to that
   function. Exactly what this does depends on the signal in question.





   THIS IS NOT VERY HELPFUL. REFER TO A FILE?








long duh_signal_render_samples(
   DUH_SIGNAL_SAMPINFO *signal_sampinfo,
   float volume, float delta,
   long size, sample_t **samples
);

   Renders 'size' samples of the signal for which signal_sampinfo was set up.
   See duh_render() and DUH_RENDER_SAMPLES for details on the 'volume' and
   'delta' parameters. This function will return the number of samples
   generated, which will be fewer than 'size' if the signal ends.

   Sometimes you can pass the array of sample buffers which was passed to
   your function, and process the data in place. Other times you will have to
   set up the array of sample buffer pointers yourself, making sure each
   buffer can hold 'size' samples. Below is some code to do that. Note that
   we prefix some variable names with sub-, so they don't clash with the
   parameters to the function that would typically contain this code.

      sample_t **subsamples;
      int n;
      long subsize;

      subsamples = malloc(n_channels * sizeof(*subsamples));

      if (!subsamples)
         return 0;

      subsamples[0] = malloc(size * n_channels * sizeof(*subsamples[0]));

      if (!subsamples[0]) {
         free(subsamples);
         return 0;
      }

      for (n = 1; n < n_channels; n++)
         subsamples[n] = subsamples[n-1] + size;

      subsize = signal_render_samples(
         subsampinfo,
         volume, delta,
         size, subsamples
      );

      /* Process the samples here. */

      free(subsamples[0]);
      free(subsamples);

      return subsize;


void duh_signal_end_samples(DUH_SIGNAL_SAMPINFO *signal_sampinfo);

   Call this when you have finished with a DUH_SIGNAL_SAMPINFO struct. It
   will free all memory used by the struct.


**************************
*** Resampling Helpers ***
**************************


   The DUH player library provides a versatile resampling system. The sample
   signal type uses it, and it is available for use in any of your signals.

   Be warned that the resampler may overrun the memory you specify by up to
   DUMB_EXTRA_SAMPLES samples, so you must allocate these samples and set
   them to appropriate values when you load your signal. Generally, if you
   are going to loop, set them to reflect the samples at the loop start
   point; if you are not going to loop, set them to zero.

   DUMB_EXTRA_SAMPLES is defined as follows:

      #define DUMB_EXTRA_SAMPLES 3


extern int resampling_quality;

   Allows you to control the quality of all resampling that takes place. This
   may be set to any value from 0 to 4. Higher values will sound better, but
   lower values will use up less processor time.

                   |     --___
      0 - Aliasing |__---     __
                   |            ___--

                            |  __
      1 - Linear resampling | /  \  /\
                            |/    \/  \__

      2 - Linear resampling / linear average

      3 - Quadratic / linear average

      4 - Cubic / linear average

   Level 0 has very noticeable unwanted overtones. It will occasionally
   produce satisfactory results for noisy signals, but usually you will
   want to pay for the extra processor time (which isn't much) and go
   for Level 1.

   Levels 1 and 2 are already pretty good. When resampling down a few
   octaves, however, you will begin to notice unwanted high frequencies.
   These can be eliminated by switching to Levels 3 or 4.

   When Level 2 or higher are selected, a linear average function is used for
   speeding the wave up. Instead of skipping samples, all samples in the
   interval will be averaged. The interval is also smoothed at the edges.
   This will be especially beneficial when increasing the pitch by several
   octaves.

   Levels 3 and 4 are both smooth curves to the eye. They both give
   extremely good performance, but you may sometimes notice the difference
   when reducing the pitch of a sample by several octaves, where Level 3
   may exhibit unwanted high frequencies.








   NOTE: WHAT IS THE DEFAULT? (2 at the moment, but might change. Config?)













long dumb_resample(
   sample_t *src, long *_src_pos, int *_src_subpos,
   long src_start, long src_end,
   sample_t *dst, long dst_size,
   float delta, int *_dir,
   DUMB_RESAMPLE_PICKUP pickup, void *pickup_data
);

   This is the resampling function. It takes an array of source samples and
   fills as much of the destination array as it can for you. Its operation is
   quite complicated, so pay attention.

   All the parameters prefixed with an underline (_) are pointers to the
   fields they describe. This means the function can modify the variables you
   pass, but you have to prefix the variable with an ampersand (&) when
   passing it. If you get warnings about ints being converted to pointers
   without casts, you have most likely forgotten an ampersand somewhere.

   'src' points to the source sample data. It should point to the beginning
   of the sample, even if you are starting your resampling from somewhere in
   the middle. Do not break this rule unless you know what you're doing.

   '_src_pos' and '_src_subpos' together represent the current position in
   the sample. When you first call the function, they represent the starting
   position. Once the function has done its work, they will represent the
   point at which the resampling stopped. '_src_pos' is measured in samples.
   '_src_subpos' represents how far between samples we are, and ranges from
   0 to 65535 - so if _src_subpos is 65535, we are very nearly on to the next
   sample.

   Once _src_pos and _src_subpos have been modified by this function, you can
   pass them to the function again and the resampling will continue
   seamlessly. This is important, as you will hardly ever get to render all
   your samples in one go. Typically you will make one call to this function
   from within your render_samples function.

   This function does not need to know the size of the source buffer as such.
   Instead, it knows src_start and src_end, which are start and end points.
   The end point actually points to the first sample not to use, following
   the usual start-inclusive end-exclusive convention. In general, the
   resampling will stop when src_pos tries to pass one of these.

   WARNING: dumb_resample() may read up to DUMB_EXTRA_SAMPLES samples beyond
            src_end. Make sure the memory belongs to you.

   The _dir parameter should be either 1 or -1, and tells this function
   whether to go forwards or backwards respectively through the source sample
   buffer. If _dir is 0, nothing will happen - resampling has stopped
   permanently. Any other values of _dir will have unpredictable results, and
   the debugging library will abort if you try to use them.

      If _dir is 1, then _src_pos will only be tested against src_end.
      If _dir is -1, then _src_pos will only be tested against src_start.

   That means you can set src_start and src_end to your loop points, and
   start playing from the beginning of the sample. The resampling will
   proceed unimpeded while _src_pos is outside the loop section, provided it
   is advancing towards the loop section. The sample signal makes extensive
   use of this capability.

   The output waveform will be rendered into the buffer pointed to by dst.
   Up to dst_size samples will be generated. Fewer will be generated if the
   resampling stops permanently. The number generated will be returned. You
   can find out if resampling ended by testing the value of your direction
   variable (pointed to by _dir); if it is 0, then resampling stopped
   permanently.

   If you pass NULL for dst, no samples will be generated. However, _src_pos,
   _src_subpos and _dir will be updated as normal. The pick-up function will
   be called as necessary; you should pass the usual 'src' parameter so it
   can be passed to your pick-up function. (See below for information on
   pick-up functions.) The function returns the number of samples that would
   have been generated, had dst pointed somewhere. The operation of
   do_resample() when dst is NULL is exactly the same as that when dst points
   somewhere. However, do_resample() is much faster in this case; you should
   use it when volume is 0, or when you are culling quieter samples to gain
   execution speed.

   If delta is 1.0f, the destination is the same speed as the source. Greater
   values cause the sample to speed up, and lesser values cause the sample to
   slow down. delta should always be positive.

   We have covered most of the parameters. There are only two left - pickup
   and pickup_data. The simplest usage is to set these both to NULL. Then, if
   resampling stops, it stops permanently. Use this if your sample will not
   loop.

   Alternatively, you may wish to write a pick-up function. Your pick-up
   function will take control whenever _src_pos tries to pass the start or
   end points. You can use it for looping, or for a multitude of other tasks.
   The typedef is as follows:

      typedef int (*DUMB_RESAMPLE_PICKUP)(
         sample_t *src, long *_src_pos, int *_src_subpos,
         long *_src_start, long *_src_end,
         int dir,
         void *data
      );

   You are passed the source buffer, in case you want to fill it with new
   samples. You have the _src_pos, _src_subpos, _src_start and _src_end
   pointers, should you need to change the values to which they point. You
   also have dir, but note that it is not a pointer. Instead, you should
   return the new direction, or 0 to stop the resampling permanently.

   The data parameter is a copy of pickup_data as passed to dumb_resample().
   This is typically used to give you access to your 'sampinfo' parameter,
   passed to your render_samples function.

   BEWARE: you must refer to and change src_pos, src_subpos, src_start and
           src_end through the parameters passed. Do not mistakenly use their
           equivalents in your struct instead. The equivalents in your struct
           will eventually be updated, but they will not be accurate during
           the pick-up function.

   On entry to this function, src_pos and src_subpos will have overrun by a
   small amount. When changing them, you should preserve this overrun. The
   following examples will do this for you:

      If you are executing a simple loop, subtract (or add) the difference
      between the loop points from (or to) src_pos. Do not simply set src_pos
      to the other loop point.

         *_src_pos -= *_src_end - *_src_start;

      If you are executing a ping-pong loop, you need to reflect the pointer
      off the loop boundary. To reflect off the loop end point:

         *_src_pos = (*_src_end << 1) - 1 - *_src_pos;
         *_src_subpos ^= 65535;
         dir = -1;

      To reflect off the loop start point:

         *_src_pos = (*_src_start << 1) - 1 - *_src_pos;
         *_src_subpos ^= 65535;
         dir = 1;

      In each case, don't forget to return the new direction correctly!

   An ideal example of a pick-up function can be found in src/sample.c.

   Of course there is more that can be done with a pick-up function. Enjoy
   experimenting!


************************
*** DUH Construction ***
************************


DUH *make_duh(
   long length,
   int n_signals,
   DUH_SIGTYPE_DESC *desc[],
   void *signal[]
);

   Constructs a DUH from its component parts. Use this function if you are
   writing a function to load a music file format other than .duh. Indeed,
   this function is used internally to load IT files.

   Before you call this function, you must do some preparation. Your DUH will
   contain a fixed number of signals; pass this number as n_signals. Each
   signal will have a DUH_SIGTYPE_DESC struct, and you pass an array of
   pointers to these. The DUH_SIGTYPE_DESC struct contains the following
   fields:

      long type;
      DUH_LOAD_SIGNAL    load_signal;
      DUH_START_SAMPLES  start_samples;
      DUH_SET_PARAMETER  set_parameter;
      DUH_RENDER_SAMPLES render_samples;
      DUH_END_SAMPLES    end_samples;
      DUH_UNLOAD_SIGNAL  unload_signal;

   The structs must be in permanent memory, i.e. either global or static, and
   not modified later; however, the array of pointers can be destroyed as
   soon as the function returns.

   The values of 'type' and 'load_signal' are irrelevant; set them to 0 and
   NULL respectively, unless you are using the DUH_SIGTYPE_DESC struct
   elsewhere.

   Because 'load_signal' is never used, you must provide an array of pointers
   to the data that 'load_signal' would otherwise have returned. This will
   be passed to the other functions as 'signal'. Once again, the array of
   pointers can be destroyed as soon as the function returns. As for the
   pointers themselves:

   If an 'unload_signal' function is provided, then that will be used to
   deallocate the pointer when you finally destroy the DUH. If the function
   is not provided, then you are responsible for deallocating any memory
   referenced by that pointer yourself. This applies individually to each
   signal.

   If this function fails and returns NULL, then any 'unload_signal'
   functions you provided will have been called. You do not have any extra
   work to do if it fails.


********************************
*** Allegro Packfile Support ***
********************************


void dumb_register_packfiles(void);

   This function registers the Allegro PACKFILE input module for use by
   DUMBFILEs. PACKFILE structs and their corresponding functions, as defined
   by Allegro's header file allegro.h, will be used internally for all
   DUMBFILE input (unless opened with dumbfile_open_ex()).

   This must be called before dumbfile_open() is used, or else an alternative
   system must be registered (see register_dumbfile_system() and
   dumb_register_stdfiles()).


DUMBFILE *dumbfile_open_packfile(PACKFILE *p);

   If you have an Allegro PACKFILE struct representing an open file, you can
   call this if you wish to read from it using a DUMBFILE. This is useful
   when you need to pass a DUMBFILE struct to a library function, to read an
   embedded music file for example. When you close the DUMBFILE, you can
   continue using the PACKFILE struct to read what follows the embedded data.


***********************************************
*** Allegro Datafile Registration Functions ***
***********************************************


void register_dat_duh(void);

   If you wish to put a DUH file in an Allegro datafile, you must use "DUH "
   for the type. The grabber will have a box for the type when you insert a
   new object. The grabber will treat the DUH file as binary data, which
   means the datafile will contain an exact copy of the DUH file on disk.





   TODO: make it possible to choose the type...





   You must then call register_dat_duh() in your program before you load the
   datafile. Once you've done this, you'll be able to access the DUH using
   the usual datafile[n].dat notation. You do not need to call unload_duh()
   on this DUH; unload_datafile() will do that for you.

   If you need to check the type of the object for whatever reason, you can
   use DAT_DUH, defined as follows:

      #define DAT_DUH DAT_ID('D','U','H',' ')

   The following example iterates through all the DUHs in disan.dat:

      DATAFILE *dat;
      int n;

      register_dat_duh();
      dat = load_datafile("disan.dat");

      for (n = 0; dat[n].type != DAT_END; n++) {
         if (dat[n].type == DAT_DUH) {
            DUH *duh = dat[n].dat;
            /* Insert code here to play 'duh' or whatever you want to do. */
         }
      }

      unload_datafile(dat);


void register_dat_it(void);

   Inserting an IT file in an Allegro datafile is the same as inserting a DUH
   file, except the type has to be "IT  ", the registration function is
   register_dat_it(), and the following definition is available for use
   instead of DAT_DUH:

      #define DAT_IT DAT_ID('I','T',' ',' ')

   Once the datafile is loaded, the 'dat' field indeed points to a DUH
   struct. There are no differences other than those listed above.


*************************************
*** Allegro DUH Playing Functions ***
*************************************


   The functions in this section allow you to play back a DUH through
   Allegro's sound system. You must call Allegro's install_sound() function
   before you use them.


AL_DUH_PLAYER *al_start_duh(
   DUH *duh, int n_channels, long pos, float volume, long bufsize, int freq
);

   Starts playing the specified DUH.

   An AL_DUH_PLAYER represents one instance of the DUH playing. If you wish,
   you can have two or more AL_DUH_PLAYERs going at the same time, for the
   same DUH or for different ones. Each uses one of Allegro's audio streams
   and hence one voice.

   At present, n_channels can either be 1 or 2 for monaural or stereo
   respectively. If you use the debugging library, your program will abort if
   other values are passed; otherwise weird things will happen.

   The DUH will start playing from position 'pos'. 0 represents the start of
   the DUH, and 65536 represents one second. Unlike other music systems, DUMB
   will always make sure every note is there right from the start, provided
   any custom signal types are designed properly. In other words, you can
   start a DUH at a point halfway through a long note, and you will still
   hear the long note.

   The volume is a float. 1.0f is the pseudo-maximum. If you pass 1.0f, any
   properly designed DUH file will play nice and loud, but will not clip. You
   can pass a greater volume if you like, but be prepared for clipping to
   occur. Of course you can pass smaller values to play the DUH more quietly,
   and this will also resolve clipping issues in badly designed DUH files.

   You will need to pass the AL_DUH_PLAYER to other functions when you need
   to stop or pause the DUH, change its volume, or otherwise modify the way
   it is playing. You will also need to pass it to al_poll_duh() at regular
   intervals; if the sound is choppy, try calling al_poll_duh() more often.

   'bufsize' is the number of samples that will be rendered at once. 1024 is
   a suitable value for most purposes.





   NOTE: IS THAT TRUE ON ALL SYSTEMS?





   The greater this is, the less often you will have to call al_poll_duh() -
   but when al_poll_duh() decides to fill the buffer, it will take longer
   doing so. If your game exhibits regular brief freezes, try reducing the
   buffer size. If the sound is choppy, however, you may have to increase it.

   'freq' specifies the sampling frequency at which the DUH should be
   rendered. At present there is no (official and portable) way of knowing
   the frequency at which Allegro is mixing - but if you do know that
   frequency, passing it here will give the highest quality sound. If you
   reduce it, the DUH will sound less crisp but use less processor time.

   When you have finished, you must pass the AL_DUH_PLAYER to al_stop_duh()
   to free up memory. Do not destroy the DUH beforehand.

   There is no real need to check the return value from this function. The
   other functions can be called safely with null pointers, so if there is a
   problem, your music will simply not play.


void al_stop_duh(AL_DUH_PLAYER *dp);

   This will stop an AL_DUH_PLAYER. You must call this when you have finished
   with it, before destroying the DUH. The pointer will no longer be valid on
   return from this function.


void al_pause_duh(AL_DUH_PLAYER *dp);

   This will pause an AL_DUH_PLAYER. Use al_resume_duh() when you want it to
   continue.


void al_resume_duh(AL_DUH_PLAYER *dp);

   Causes a paused AL_DUH_PLAYER to resume playing (see al_pause_duh()).


void al_duh_set_volume(AL_DUH_PLAYER *dp, float volume);

   This will set the volume of an AL_DUH_PLAYER. See al_start_duh() for
   details on the volume parameter.


int al_poll_duh(AL_DUH_PLAYER *dp);

   An AL_DUH_PLAYER is not interrupt-driven. That means it will not play by
   itself. You must keep it alive from your main program. Call this function
   at regular intervals. If the sound crackles, try calling it more often.
   (There is nothing you can do if Windows decides to play with the hard
   disk; that will make your sound crackle no matter what you do.)

   Normally this function will return zero. However, if it returns nonzero,
   that means the AL_DUH_PLAYER will not generate any more sound. Indeed the
   underlying audio stream and DUH_RENDERER have been destroyed. When this
   happens, you can call al_stop_duh() whenever you wish - but you do not
   have to. Note that this function will wait two buffers' worth of samples
   before taking this action, allowing Allegro to mix the trailing sound
   before the audio stream is destroyed. In other words, your music will not
   be cut off prematurely.

   In case you were wondering, it is definitely not safe to call
   al_poll_duh() from an interrupt context. Not only is no part of DUMB
   locked in memory, but many signals and many core library functions
   allocate and free their memory on a call-by-call basis! Remember that any
   disk access that occurs in interrupt context is likely to crash the
   machine. (This limitation only applies to DOS at present, and is due to
   the fact that the DOS file access functions are not re-entrant.
   Multitasking systems are generally safe. However, even if you do not think
   you are targeting DOS, it is worth considering that calling this function
   regularly from your main loop is likely to be much more efficient than
   relying on task switching to do it for you.)


long al_duh_get_position(AL_DUH_PLAYER *dp);

   Tells you what position an AL_DUH_PLAYER is up to, or -1 if it is invalid
   (perhaps owing to lack of memory). As usual, 65536 is one second. Note
   that this is a whole number, whereas a fractional part is stored
   internally; the sample will not be continuous if you terminate the
   AL_DUH_PLAYER and then reinitiate it with the same position. Furthermore,
   note that Allegro will not have mixed in all the sound up to this point;
   if you wait for this to reach a certain position and then terminate the
   AL_DUH_PLAYER, the sound will cut off too early.








   NOTE: HOW TO GET AROUND THIS?








DUH_RENDERER *al_duh_get_renderer(AL_DUH_PLAYER *dp);

   This function returns to you the DUH_RENDERER underlying the specified
   AL_DUH_PLAYER. It has no practical use in this release, but it
   TODO: Write this. Also see if it should go in howto.txt.


******************
*** Conclusion ***
******************


I conclude that... DUMB is the bestest music player in the world because...
Complete this sentence in fifteen words or fewer... D'OH!


Ben Davis
entheh@users.sf.net
IRC EFnet #dumb
See readme.txt for details on using IRC.
